package main

import (
	"fmt"
	"strconv"
	"time"

	myrpc "github.com/2217263633/kakarpc"
	_ "github.com/go-sql-driver/mysql"
	"github.com/jmoiron/sqlx"
	"github.com/wonderivan/logger"
)

type CusMysql struct {
	DB *sqlx.DB
}

func Connect() error {
	url := "root:kaPass@.vcf3b@tcp(125.91.108.68:3306)/stream?parseTime=true&timeout=5s"
	database, err := sqlx.Open("mysql", url)

	if err != nil {
		logger.Info("err", err)
		return err
	}

	CusMySql.DB = database
	return nil
}

func (cu *CusMysql) assQuery(sql string) ([]map[string]interface{}, error) {
	var ret []map[string]interface{} = make([]map[string]interface{}, 0)
	rows, err := cu.DB.Query(sql)
	if err != nil {
		return ret, err
	}
	defer rows.Close()

	cols, _ := rows.Columns()
	types, _ := rows.ColumnTypes()
	for rows.Next() {
		buff := make([]interface{}, len(cols))
		data := make([][]byte, len(cols)) //数据库中的NULL值可以扫描到字节中
		for i := range buff {
			buff[i] = &data[i]
		}
		rows.Scan(buff...) //扫描到buff接口中，实际是字符串类型data中
		//将每一行数据存放到数组中
		dataKv := make(map[string]interface{}, len(cols))

		for k, col := range data { //k是index，col是对应的值
			str := string(col)
			// logger.Info(types[k].DatabaseTypeName(), str, cols[k])
			switch types[k].DatabaseTypeName() {
			case "UNSIGNED INT":
				dataKv[cols[k]], _ = strconv.Atoi(str)
			case "INT":

				dataKv[cols[k]], _ = strconv.Atoi(str) //int64(binary.BigEndian.Uint64(col)) //strconv.Atoi(str)

			case "BIGINT":
				dataKv[cols[k]], _ = strconv.Atoi(str)
			case "DOUBLE":
				dataKv[cols[k]], _ = strconv.ParseFloat(str, 32)
			case "TIMESTAMP":
				_time, _ := time.ParseInLocation(time.RFC3339, str, time.Local)
				_time = _time.Add(time.Hour * 8)
				dataKv[cols[k]] = _time
			case "FLOAT":
				dataKv[cols[k]], _ = strconv.ParseFloat(str, 32)
			default:
				dataKv[cols[k]] = str
			}
		}
		ret = append(ret, dataKv)
	}
	return ret, err
}

func (cu *CusMysql) QuerySql(sql string) ([]map[string]interface{}, error) {
	resu, err := cu.assQuery(sql)
	return resu, err
}

func (cu CusMysql) QueryIdlimit1(tableName string) ([]map[string]interface{}, error) {
	sql := fmt.Sprintf("select id from %s  limit 0,1", tableName)
	rows, err := cu.DB.Query(sql)
	var ret []map[string]interface{} = make([]map[string]interface{}, 0)
	if err != nil {
		return ret, err
	}
	defer rows.Close()
	return ret, err
}

// resu ,total,size,err
func (cu CusMysql) PageSql(sql myrpc.SqlStruct) ([]map[string]interface{}, int, int, error) {
	if sql.Page == 0 {
		sql.Page = 1
	}
	if sql.Size == 0 {
		sql.Size = 10
	}

	_countSql := fmt.Sprintf(`select count(*)total from %s  %s`, sql.Tabel_name, sql.Where)
	count, err := cu.assQuery(_countSql)
	if err != nil {
		count = make([]map[string]interface{}, 0)
		_val := make(map[string]interface{})
		_val["total"] = -1
		count = append(count, _val)
	}
	resu, err := cu.assQuery(sql.ToString() + fmt.Sprintf(` LIMIT %d,%d`, (sql.Page-1)*sql.Size, sql.Size))
	if err != nil {
		return resu, 0, sql.Size, err
	}

	return resu, count[0]["total"].(int), sql.Size, err
}

func (cu CusMysql) DropTable(tableName string) {
	cu.DB.Exec("DROP TABLE " + tableName)
}

func (cu CusMysql) InsertTable(sql string) error {
	_, err := cu.DB.Exec(sql)
	return err
}

func (cu CusMysql) InsertTableId(sql string) (int, error) {
	res, err := cu.DB.Exec(sql)

	if err != nil {
		logger.Info(sql, err)
		return 0, err
	}
	id, err := res.LastInsertId()
	return int(id), err
}

// where sql is complate statement
func (cu CusMysql) InsertTablesSql(table_name string, columns_sql string, values_sql string, where_sql string) error {
	sql := fmt.Sprintf(`insert into %s (%s)
	values(%s) %s
	`, table_name, columns_sql, values_sql, where_sql)
	_, err := cu.DB.Exec(sql)
	if err != nil {
		logger.Info(sql, err)
	}
	return err
}

var CusMySql = &CusMysql{}
